﻿using System;
using System.Configuration;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Timer = System.Timers.Timer;

namespace com.bodurov.CameraToJpegFilesUI
{
    public partial class MainWindow : Window
    {
        public readonly string FILES_FOLDER = ConfigurationManager.AppSettings["FILES_FOLDER"] ?? "../../files/";
        public readonly int REFRESH_CAMERA_MS = ParseInt(ConfigurationManager.AppSettings["REFRESH_CAMERA_MS"], 200);

        private Camera _camera;
        private Timer _filesTimer;
        private Timer _previewTimer;
        private bool _isRunning;
        private bool _isCameraEnabled = true;
        private DateTime _lastVideoCameraView;

        public MainWindow()
        {
            this.Closing += MainWindow_Closing;

            InitializeComponent();

            SetupTimers();

            SetupCapture();

        }

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            this._isCameraEnabled = false;
            if (this._camera != null) this._camera.Dispose();
            this._previewTimer.Stop();
            this._filesTimer.Stop();
        }

        private void SetupCapture()
        {
            try
            {
                this._camera = new Camera(160, 120, 24);
                this._camera.ImageCaptured += Camera_ImageCaptured;
                Task.Factory.StartNew(() => this._camera.RequestPointerToBitmap());
            }
            catch (Exception ex)
            {
                Trigger.IsEnabled = false;
                MessageBox.Show(ex.Message + "\n" + ex);
            }
        }


        private void Camera_ImageCaptured(object sender, ImageCapturedEventArgs e)
        {
            var now = DateTime.Now;
            var span = now - _lastVideoCameraView;
            var showOnCamera = (this._isCameraEnabled && span.TotalMilliseconds >= REFRESH_CAMERA_MS);
            if (showOnCamera) _lastVideoCameraView = now;
            Bitmap bitmap = null;
            if (this._isRunning)
            {

                bitmap = new Bitmap(this._camera.VideoWidth, this._camera.VideoHeight, this._camera.Stride, PixelFormat.Format24bppRgb, e.PointerToImage);

                if (showOnCamera) ShowBitmapOnCamera((Bitmap)bitmap.Clone());
                ((Bitmap)bitmap.Clone()).Save(FILES_FOLDER + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_fff") + ".jpg", ImageFormat.Jpeg);
                this._camera.FreePointerMemoryAndRequestNew();
            }
            else if (showOnCamera)
            {
                bitmap = new Bitmap(this._camera.VideoWidth, this._camera.VideoHeight, this._camera.Stride, PixelFormat.Format24bppRgb, e.PointerToImage);
                ShowBitmapOnCamera(bitmap);
            }

        }

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr handle);

        private IntPtr _videoCamBitmapPointer = IntPtr.Zero;
        private void ShowBitmapOnCamera(Bitmap bitmap)
        {
            if (this._videoCamBitmapPointer != IntPtr.Zero)
            {
                DeleteObject(this._videoCamBitmapPointer);
            }
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
            this._videoCamBitmapPointer = bitmap.GetHbitmap();

            Dispatcher.Invoke(DispatcherPriority.Normal,
                                   new Action<IntPtr>(ptr =>
                                   {
                                        CameraView.Source = Imaging.CreateBitmapSourceFromHBitmap(
                                                ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

                                   }), this._videoCamBitmapPointer);

        }
        #region File Listener

        private void SetupTimers()
        {
            this._filesTimer = new Timer(2000);
            this._filesTimer.Elapsed += (s, e) => SetCurrentFiles();
            this._filesTimer.Start();
            SetCurrentFiles();

            this._previewTimer = new Timer(REFRESH_CAMERA_MS);
            this._filesTimer.Elapsed += (s, e) => { if (!this._isRunning) this._camera.RequestPointerToBitmap(); };
            this._filesTimer.Start();
        }

        private void SetCurrentFiles()
        {
            Dispatcher.Invoke(DispatcherPriority.Normal,
                                new Action(() =>
                                {
                                    Files.ItemsSource =
                                        Directory.GetFiles(FILES_FOLDER)
                                                    .Select(file => new FileInfo(file))
                                                    .Select(fi => new { fi.Name, fi.LastWriteTime })
                                                    .OrderBy(f => f.Name).ToArray();

                                    Files.UpdateLayout();
                                }));

        }
        #endregion


        private void Trigger_Click(object sender, RoutedEventArgs e)
        {
            this.OnOff();
        }
        private void ViewCamera_Click(object sender, RoutedEventArgs e)
        {
            this.EnableCamera();
        }

        private void EnableCamera()
        {
            ViewCamera.IsEnabled = false;
            this._isCameraEnabled = true;
        }
        private void DisableCamera()
        {
            ViewCamera.IsEnabled = true;
            this._isCameraEnabled = false;
        }
        private void OnOff()
        {
            Trigger.Content = this._isRunning ? "Start Saving Images" : "Stop Saving Images";
            this._isRunning = !this._isRunning;

            if (this._isRunning) StartProcessing();
        }

        private void StartProcessing()
        {
            this.EnableCamera();
            Task.Factory.StartNew(() =>
            {
                try
                {
                    foreach (var file in Directory.GetFiles(FILES_FOLDER))
                    {
                        File.Delete(file);
                    }
                    this._camera.RequestPointerToBitmap();
                }
                catch { }
            });
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Space)
            {
                this.OnOff();
            }
        }

        private void SetPreviewImage(string filePath)
        {
            if (!File.Exists(filePath)) return;
            using (var stream = File.OpenRead(filePath))
            {
                CameraView.Source = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            }
        }

        private void ViewImageButton_Click(object sender, RoutedEventArgs e)
        {
            var elem = e.OriginalSource as FrameworkElement;
            if (elem != null)
            {
                var fileName = elem.DataContext as string;
                if (!fileName.EndsWith(".jpg", true, Thread.CurrentThread.CurrentCulture) && !fileName.EndsWith(".jpeg", true, Thread.CurrentThread.CurrentCulture))
                {
                    MessageBox.Show("This is not jpeg image");
                }
                else
                {
                    this.DisableCamera();
                    SetPreviewImage(Path.GetFullPath(FILES_FOLDER + fileName));
                }
            }
        }

        private static int ParseInt(string str, int defaultValue)
        {
            if (String.IsNullOrEmpty(str)) return defaultValue;
            int i;
            return Int32.TryParse(str, out i) ? i : defaultValue;
        }
    }
}
